﻿/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace DaveSexton.DocProject.Sandcastle
{
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
	public sealed partial class Help2xProjectEditorDialog : Form
	{
		#region Public Properties
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "x")]
		public Help2xProject Help2xProject
		{
			get
			{
				return helpProject;
			}
		}
		#endregion

		#region Private / Protected
		private static readonly Guid DialogGuid = new Guid("C029F115-8B76-4eb6-B31F-F29234A07AB6");

		private readonly Help2xProject helpProject;
		private readonly DialogSettings<Help2xProjectEditorDialog> dialogSettings;
		private readonly bool initializing;
		private bool htmlTopicsLoaded;
		private List<string> htmlTopics;
		private BuildContext buildAllTopicsContext;
		private string currentPageInEditMode;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="Help1xProjectEditorDialog" /> class.
		/// </summary>
		public Help2xProjectEditorDialog(Help2xProject helpProject)
			: this()
		{
			if (helpProject == null)
				throw new ArgumentNullException("helpProject");

			this.helpProject = helpProject;

			dialogSettings = new DialogSettings<Help2xProjectEditorDialog>(this, DialogGuid);
			dialogSettings.Load();

			initializing = true;

			DataBind();

			initializing = false;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="Help2xProjectEditorDialog" /> class.
		/// </summary>
		private Help2xProjectEditorDialog()
		{
			InitializeComponent();
		}
		#endregion

		#region Methods
		private void DataBind()
		{
			outputEnabledCheckBox.DataBindings.Add("Checked", helpProject, "OutputEnabled");

			defaultPageComboBox.DataBindings.Add("Text", helpProject, "DefaultPageTopicFile");
			homePageComboBox.DataBindings.Add("Text", helpProject, "HomePageTopicFile");

			attributesGrid.Metadata = helpProject.Attributes;

			ShowAboutPageImage();
		}

		[System.Diagnostics.DebuggerHidden]
		private void ShowAboutPageImage()
		{
			try
			{
				aboutPictureBox.Image = Image.FromFile(helpProject.AboutImageFile);
			}
			catch (System.IO.FileNotFoundException) { }
			catch (System.IO.DirectoryNotFoundException) { }
		}

		private void EnsureTopicsLoaded()
		{
			if (htmlTopics == null)
			{
				htmlTopics = new List<string>();

				SandcastleBuildEngine engine = (SandcastleBuildEngine) BuildController.GetEngineForProject(helpProject.DocProject);

				try
				{
					buildStatusLabel.Visible = true;
					buildProgressBar.Visible = true;

					CapturedBuildTrace trace = new CapturedBuildTrace();
					trace.StatusChanged += (s, e) => buildStatusLabel.Text = trace.Status;
					trace.ProgressChanged += (s, e) => buildProgressBar.Value = trace.Progress;

					Engine.BuildStepCollection steps = new Engine.BuildStepCollection(engine);

					buildAllTopicsContext = new BuildContext(engine, trace, steps);

					string tocFile;
					if (engine.BuildToc(buildAllTopicsContext, false, false, true, true, out tocFile))
					{
						foreach (string file in TopicManagement.TopicsDocument.GetAllFiles(tocFile))
							htmlTopics.Add(file + ".htm");

						htmlTopics.Sort(StringComparer.OrdinalIgnoreCase);

						htmlTopicsLoaded = true;

						EnsureFilled(defaultPageComboBox, helpProject.DefaultPageTopicFile);
						EnsureFilled(homePageComboBox, helpProject.HomePageTopicFile);
					}
					else
					{
						Log.Exception(null, "Error building TOC for Help 2.x Settings dialog.  The build trace output follows." +
							Environment.NewLine + Environment.NewLine + trace.Output);

						errors.SetError(homePageComboBox, trace.Output);
					}
				}
				finally
				{
					bool leaveStatus = !buildAllTopicsContext.SuccessfullyExecutedAllRequiredSteps;

					buildStatusLabel.Visible = leaveStatus;
					buildProgressBar.Visible = leaveStatus;

					buildAllTopicsContext = null;
				}

				htmlTopics.Insert(0, string.Empty);
			}
		}

		private void EnsureFilled(ComboBox combo, string defaultTopic)
		{
			if (combo.DataSource == null && htmlTopicsLoaded)
			{
				combo.DataSource = new BindingSource(htmlTopics, null);
				combo.SelectedItem = defaultTopic;
			}
		}

		/// <remarks>A synchronization mechanism is not required since this class is not thread-safe to begin with; i.e., 
		/// Calls must be made from a single thread only (typically, the UI thread).  Therefore, it's not possible for 
		/// this method to execute while code in the <see cref="EnsureTopicsLoaded"/> method is executing, although since
		/// the build engine uses <see cref="System.Windows.Forms.Application.DoEvents"/>, this method may be called before the 
		/// <see cref="EnsureTopicsLoaded"/> method returns to its caller; however, it's not possible for any race conditions 
		/// to occur when checking the <see cref="buildAllTopicsContext"/> field since all calls to this class must originate 
		/// from the same thread - only one member may be 'executing' at any given time.</remarks>
		public void CancelBuildAllTopics(bool wait)
		{
			IDocProject project = helpProject.DocProject;

			if (buildAllTopicsContext != null)
			{
				buildAllTopicsContext.Cancel();

				if (wait)
				{
					System.Diagnostics.Debug.WriteLine("Waiting to cancel build for " + project.Name, "Help2xProject");

					buildAllTopicsContext.WaitHandle.WaitOne();

					System.Diagnostics.Debug.WriteLine("Build canceled for " + project.Name, "Help2xProject");
				}
			}
		}

		private void LoadPageHtml()
		{
			string page = pagesComboBox.SelectedItem as string;

			if (page == currentPageInEditMode)
				return;

			switch (page)
			{
				case "About Page":
					if (SavePageHtml())
					{
						editor.Html = helpProject.AboutPageInfoHtml;
						editorPanel.Enabled = true;
					}
					else
						pagesComboBox.SelectedItem = currentPageInEditMode;
					break;
				case "Navigation Failure Page":
					if (SavePageHtml())
					{
						editor.Html = helpProject.NavFailPageHtml;
						editorPanel.Enabled = true;
					}
					else
						pagesComboBox.SelectedItem = currentPageInEditMode;
					break;
				case "Empty Index Term Page":
					if (SavePageHtml())
					{
						editor.Html = helpProject.EmptyIndexTermHtml;
						editorPanel.Enabled = true;
					}
					else
						pagesComboBox.SelectedItem = currentPageInEditMode;
					break;
				default:
					if (SavePageHtml())
					{
						editor.Html = string.Empty;
						editorPanel.Enabled = false;
					}
					else
						pagesComboBox.SelectedItem = currentPageInEditMode;
					break;
			}

			currentPageInEditMode = page;
		}

		private string GetPageHtmlAsXml(out Exception exception)
		{
			try
			{
				exception = null;

				string html = DaveSexton.Controls.HtmlToXmlConverter.ConvertFragment(editor.Body, true);

				System.Xml.XmlDocument document = new System.Xml.XmlDocument();
				document.LoadXml("<root>" + html + "</root>");
				string text = document.InnerText;

				if (text.Trim().Length == 0)
					return null;
				else
					return html;
			}
			catch (ArgumentException ex)
			{
				exception = ex;
			}
			catch (System.Xml.XmlException ex)
			{
				exception = ex;
			}

			return null;
		}

		private bool SavePageHtml()
		{
			Exception ex = null;

			if (!string.IsNullOrEmpty(currentPageInEditMode))
			{
				string html = GetPageHtmlAsXml(out ex);

				if (ex == null)
				{
					if (html != null)
						html = html.Trim();

					switch (currentPageInEditMode)
					{
						case "About Page":
							helpProject.AboutPageInfoHtml = html;
							break;
						case "Navigation Failure Page":
							helpProject.NavFailPageHtml = html;
							break;
						case "Empty Index Term Page":
							helpProject.EmptyIndexTermHtml = html;
							break;
					}

					errors.SetError(editorPanel, null);
				}
				else
				{
					errors.SetError(editorPanel, ex.Message);
				}
			}

			return ex == null;
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
			{
				if (components != null)
					components.Dispose();

				dialogSettings.Dispose();
			}
			base.Dispose(disposing);
		}
		#endregion

		#region Event Handlers
		protected override void OnShown(EventArgs e)
		{
			base.OnShown(e);

			EnsureTopicsLoaded();
		}

		protected override void OnFormClosing(FormClosingEventArgs e)
		{
			if (!SavePageHtml())
				e.Cancel = true;
			else
			{
				dialogSettings.Save();
				CancelBuildAllTopics(true);
			}

			base.OnFormClosing(e);
		}

		private void okButton_Click(object sender, EventArgs e)
		{
			if (ValidateChildren())
				DialogResult = DialogResult.OK;
		}

		private void pagesComboBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			LoadPageHtml();
		}

		private void editor_HtmlChanged(object sender, DaveSexton.Controls.HtmlChangedEventArgs e)
		{
			SavePageHtml();
		}

		private void defaultPageComboBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (!initializing && homePageComboBox.SelectedIndex < 1 && homePageComboBox.Items.Count > defaultPageComboBox.SelectedIndex)
				homePageComboBox.SelectedIndex = defaultPageComboBox.SelectedIndex;
		}

		private void homePageComboBox_SelectedIndexChanged(object sender, EventArgs e)
		{
			if (!initializing && defaultPageComboBox.SelectedIndex < 1 && defaultPageComboBox.Items.Count > homePageComboBox.SelectedIndex)
				defaultPageComboBox.SelectedIndex = homePageComboBox.SelectedIndex;
		}

		private void defaultPageComboBox_DropDown(object sender, EventArgs e)
		{
			EnsureFilled(defaultPageComboBox, helpProject.DefaultPageTopicFile);
		}

		private void homePageComboBox_DropDown(object sender, EventArgs e)
		{
			EnsureFilled(homePageComboBox, helpProject.HomePageTopicFile);
		}

		private void changeImageButton_Click(object sender, EventArgs e)
		{
			using (OpenFileDialog dialog = new OpenFileDialog())
			{
				dialog.CheckFileExists = true;
				dialog.CheckPathExists = true;
				dialog.Filter = "Image Files (*.jpg;*.jpe*;*.gif;*.bmp;*.png)|*.jpg;*.jpe*;*.gif;*.bmp;*.png";
				dialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
				dialog.Title = "Import Image";

				if (dialog.ShowDialog(this) == DialogResult.OK)
				{
					helpProject.ImportAboutPageImage(dialog.FileName);
					ShowAboutPageImage();
				}
			}
		}
		#endregion
	}
}
